package com.kvest.scanrecognizer.network;

import android.util.Log;
import android.util.Pair;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.kvest.scanrecognizer.network.request.StatusesRequest;
import com.kvest.scanrecognizer.network.response.RecognizeResponse;
import com.kvest.scanrecognizer.network.response.StatusResponse;
import com.kvest.scanrecognizer.utility.FormatUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: Kvest
 * Date: 30.08.13
 * Time: 19:37
 * To change this template use File | Settings | File Templates.
 */
public abstract class NetworkRequestHelper {
    private static final String TAG = "NetworkRequestHelper";
    private static final String END = "\r\n";
    private static final String TWO_HYPHENS = "--";
    private static final String BOUNDARY = "aHRQEtctEmx6GjCmkgPeHFuGYvX9ZF";

    //TODO think about CONNECT_TIMEOUT value
    private static final int CONNECT_TIMEOUT = 30000;
    private static final int READ_TIMEOUT = 120000;

    public static RecognizeResponse recognize(String filePath, int rotation, String clientId) {
        //create params
        List<Pair<String, String>> params = new ArrayList<Pair<String, String>>(2);
        if (rotation != 0) {
            params.add(new Pair<String, String>(Urls.UPLOAD_ROTATE_DEGREES_PARAM, Integer.toString(rotation)));
        }
        params.add(new Pair<String, String>(Urls.CLIENT_ID_PARAM, clientId));

        List<Pair<String, String>> files = new ArrayList<Pair<String, String>>(1);
        files.add(new Pair<String, String>(Urls.UPLOAD_FILE_PARAM, filePath));

        //make request
        String response = sendMultipartFormData(Urls.UPLOAD_REQUEST_URL, params, files);

        if (response != null) {
            try {
                Gson gson = new Gson();
                return gson.fromJson(response, RecognizeResponse.class);
            } catch (JsonSyntaxException jsonSyntaxException) {
                return new RecognizeResponse(RecognizeResponse.CODE_RESPONSE_PARSE_ERROR);
            }
        } else {
            return new RecognizeResponse(RecognizeResponse.CODE_NETWORK_ERROR);
        }
    }

    public static StatusResponse[] getStatuses(String[] uids) {
        //create request params
        StatusesRequest request = new StatusesRequest(uids);

        //make request
        Gson gson = new Gson();
        String response = post(Urls.STATUSES_REQUEST_URL, gson.toJson(request));
        if (response != null) {
            return gson.fromJson(response, StatusResponse[].class);
        } else {
            return new StatusResponse[0];
        }
    }

    public static StatusResponse getStatus(String uid) {
        String url = Urls.getStatusUrl(uid);
        //make request
        String response = get(url);

        if (response != null) {
            try {
                Gson gson = new Gson();
                return gson.fromJson(response, StatusResponse.class);
            } catch (JsonSyntaxException jsonSyntaxException) {
                return new StatusResponse(StatusResponse.CODE_ERROR);
            }
        } else {
            return new StatusResponse(StatusResponse.CODE_ERROR);
        }
    }

    public static String sendMultipartFormData(String urlString,
                                               List<Pair<String, String>> params,
                                               List<Pair<String, String>> files) {
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();

            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");

            /* setRequestProperty */
            conn.setRequestProperty("Connection", "keep-alive");
            conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
            //conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary="+ BOUNDARY);
            //conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

            DataOutputStream ds = new DataOutputStream(conn.getOutputStream());
            for (Pair<String, String> param : params) {
                ds.writeBytes(TWO_HYPHENS + BOUNDARY + END);
                ds.writeBytes("Content-Disposition: form-data; name=\"" + param.first + "\"" +
                              END + END + param.second +END);
            }

            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = -1;
            for (Pair<String, String> fileDescription : files) {
                File file = new File(fileDescription.second);
                ds.writeBytes(TWO_HYPHENS + BOUNDARY + END);
                ds.writeBytes("Content-Disposition: form-data; name=\"" + fileDescription.first +
                              "\"; filename=\"" + file.getName() +"\"" + END);
                ds.writeBytes("Content-Type: application/octet-stream" + END);
                ds.writeBytes("Content-Transfer-Encoding: binary" + END);
                ds.writeBytes(END);

                FileInputStream fStream = new FileInputStream(file);
                try {
                    while((length = fStream.read(buffer)) != -1) {
                        ds.write(buffer, 0, length);
                    }
                } finally {
                    try {
                        fStream.close();
                    } catch (IOException ioException) {}
                }
                ds.writeBytes(END);
            }
            ds.writeBytes(TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + END);

            /* close streams */
            ds.flush();
            ds.close();

            if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){
                return readStreamAsString(conn.getInputStream());
            }
        } catch (OutOfMemoryError outOfMemoryError) {
            Log.e(TAG, "outOfMemoryError error " + outOfMemoryError + "(" + urlString + ")");
        } catch (Exception e) {
            Log.e(TAG, "sendMultipartFormData error " + e + "(" + urlString + ")");
        }

        return null;
    }


    public static boolean download(String uid, int format,  File file) {
        //generate download url
        String downloadUrl = Urls.getDownloadUrl(uid);

        //add format type
        downloadUrl += "?" + Urls.DOWNLOAD_FORMAT_PARAM + "=" + FormatUtils.getNameById(format);

        //download
        return getFile(downloadUrl, file);
    }

    private static boolean getFile(String urlString, File file) {
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setReadTimeout(READ_TIMEOUT);
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setRequestMethod("GET");
            conn.setUseCaches(false);
            conn.setDoInput(true);
            conn.setDoOutput(false);
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Content-Type","application/json");
            conn.setRequestProperty("Accept", "*/*");

            conn.connect();

            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                InputStream is = conn.getInputStream();
                FileOutputStream fos = new FileOutputStream(file);

                try {
                    byte[] buffer = new byte[1024];
                    int len = 0;
                    while ((len = is.read(buffer)) != -1) {
                        fos.write(buffer, 0, len);
                    }
                } finally {
                    try {
                        is.close();
                    } catch (IOException ioException) {}
                    try {
                        fos.flush();
                        fos.close();
                    } catch (IOException ioException) {}
                }

                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            Log.e(TAG, "getFile error " + e + "(" + urlString + ")");
            return false;
        }
    }

    private static String get(String urlString) {
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setReadTimeout(READ_TIMEOUT);
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setRequestMethod("GET");
            conn.setUseCaches(false);
            conn.setDoInput(true);
            conn.setDoOutput(false);
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Content-Type","application/json");
            conn.setRequestProperty("Accept", "application/json");

            conn.connect();

            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                return readStreamAsString(conn.getInputStream());
            }
        } catch (Exception e) {
            Log.e(TAG, "get error " + e + "(" + urlString + ")");
        }
        return null;
    }

    private static String post(String urlString, String params) {
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setReadTimeout(READ_TIMEOUT);
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setRequestMethod("POST");
            conn.setUseCaches(false);
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setRequestProperty("Connection", "Keep-Alive");
           // conn.addRequestProperty("Content-length", Integer.toString(params.length()));
            conn.setRequestProperty("Content-Type","application/json");
            conn.setRequestProperty("Accept", "application/json");

            OutputStream os = new BufferedOutputStream(conn.getOutputStream());
            os.write(params.getBytes());
            os.close();
            conn.connect();

            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                return readStreamAsString(conn.getInputStream());
            }
        } catch (Exception e) {
            Log.e(TAG, "post error " + e + "(" + urlString + ")");
        }
        return null;
    }

    private static String readStreamAsString(InputStream in) {
        BufferedReader reader = null;
        StringBuilder builder = new StringBuilder();
        try {
            reader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return builder.toString();
    }
}
